package com.doupi.partner.util;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * http请求处理
 * 
 * @author Super Jens Wang
 * @version 1.0 $id: PnrHttpClientHandler.java 2016年7月8日 下午2:39:54 Wang Le $
 */
public class HttpUtils {

	private final static Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);

	/**
	 * 最大http connect 数 从 配置文件中读取
	 */
	private Integer maxTotal;

	/**
	 * 每次路由http connect最大数 从 配置文件中读取
	 */
	private Integer maxPerRoute;

	/**
	 * 编码类型
	 */
	private String chartSet = "UTF-8";

	/**
	 * 是否校验host
	 */
	private boolean allowAllHostnameVerifier = false;

	/**
	 * 连接管理
	 */
	private static PoolingHttpClientConnectionManager CONNECTION_MANAGER = null;

	/**
	 * HTTP请求器
	 */
	private static CloseableHttpClient HTTP_CLIENT = null;

	/**
	 * 重写验证方法，取消检测ssl
	 */
	private final static TrustManager TRUST_ALL_MANAGER = new X509TrustManager() {
		public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
		}

		public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
		}

		public java.security.cert.X509Certificate[] getAcceptedIssuers() {
			return null;
		}
	};

	/**
	 * 信任所有host
	 */
	private final static HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER = (paramString, paramSSLSession) -> {
		try {
			Certificate[] certs = paramSSLSession.getPeerCertificates();
			return certs != null && certs[0] instanceof X509Certificate;
		} catch (SSLException e) {
			return false;
		}
	};

	public HttpUtils(Integer maxTotal, Integer maxPerRoute, String chartSet, Boolean allowAllHostnameVerifier) {
		this.maxTotal = maxTotal;
		this.maxPerRoute = maxPerRoute;
		this.chartSet = chartSet;
		this.allowAllHostnameVerifier = allowAllHostnameVerifier;
	}

	public HttpUtils(Integer maxTotal, Integer maxPerRoute) {
		this.maxTotal = maxTotal;
		this.maxPerRoute = maxPerRoute;
	}

	/**
	 * 发送请求
	 */
	public String sendPost(String url, Map<String, String> params) {
		return sendPost(url, params, null);
	}

	/**
	 * 发送字符串数据
	 */
	public String sendPost(String url, String params) {
		return sendPost(url, params, "application/x-www-form-urlencoded; charset=UTF-8");
	}

	/**
	 * 发送字符串请求，并设置contentType
	 */
	public String sendPost(String url, String postData, String contentType) {
		Map<String, String> header = new HashMap<>();
		header.put("Content-Type", contentType);
		return sendPost(url, postData, header);
	}

	/**
	 * 发送字符串请求，并设置header
	 */
	public String sendPost(String url, Object postData, Map<String, String> header) {
		LOGGER.info("请求的URL是：{}，发送的参数是：{}", url, postData);

		String result = null;
		CloseableHttpResponse response;
		HttpPost httpPost;
		try {
			CloseableHttpClient httpClient = getHttpClient(url);

			httpPost = new HttpPost(url);

			if (null != header) {
				for (Map.Entry<String, String> entry : header.entrySet()) {
					httpPost.addHeader(entry.getKey(), entry.getValue());
				}
			}

			if (postData instanceof String) {
				StringEntity postEntity = new StringEntity(postData.toString(), chartSet);
				httpPost.setEntity(postEntity);
			} else
				if (postData instanceof Map) {
					httpPost.setEntity(buildUrlEncodedFormEntity((Map<?, ?>) postData));
				}

			LOGGER.info("开始执行请求：{}", httpPost.getRequestLine());

			response = httpClient.execute(httpPost);

			HttpEntity entity = response.getEntity();
			result = EntityUtils.toString(entity, chartSet);
			EntityUtils.consume(entity);
		} catch (Throwable e) {
			LOGGER.error("请求处理失败", e);
		}

		LOGGER.info("请求结束响应信息是：{}", result);

		return result;
	}

	/**
	 * MAP类型数组转换成buildUrlEncodedFormEntity类型
	 */
	private UrlEncodedFormEntity buildUrlEncodedFormEntity(Map<?, ?> params) throws UnsupportedEncodingException {
		List<NameValuePair> nvps = new ArrayList<>();
		for (Map.Entry<?, ?> entry : params.entrySet()) {
			nvps.add(new BasicNameValuePair(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())));
		}
		return new UrlEncodedFormEntity(nvps, chartSet);
	}

	/**
	 * 获取连接
	 */
	private CloseableHttpClient getHttpClient(String url) {
		try {
			if (null == CONNECTION_MANAGER) {
				if (allowAllHostnameVerifier) {
					SSLContext sslContext = SSLContexts.custom().build();
					sslContext.init(null, new TrustManager[] { TRUST_ALL_MANAGER }, null);
					RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
					registryBuilder = registryBuilder.register("http", PlainConnectionSocketFactory.getSocketFactory());
					registryBuilder = registryBuilder.register("https", new SSLConnectionSocketFactory(sslContext, ALLOW_ALL_HOSTNAME_VERIFIER));
					Registry<ConnectionSocketFactory> registry = registryBuilder.build();
					CONNECTION_MANAGER = new PoolingHttpClientConnectionManager(registry);
				} else {
					CONNECTION_MANAGER = new PoolingHttpClientConnectionManager();
				}
				CONNECTION_MANAGER.setMaxTotal(maxTotal);
				CONNECTION_MANAGER.setDefaultMaxPerRoute(maxPerRoute);
			}

			HttpHost httpHost = URIUtils.extractHost(new URI(url));
			CONNECTION_MANAGER.setMaxPerRoute(new HttpRoute(httpHost), maxPerRoute);

			if (null == HTTP_CLIENT) {
				HTTP_CLIENT = HttpClients.custom().setConnectionManager(CONNECTION_MANAGER).build();
			}
		} catch (Exception e) {
			LOGGER.error("初始化错误", e);
		}
		return HTTP_CLIENT;
	}

}
